Logan Smith
Logan Smith
  • Видео 13
  • Просмотров 882 370
Constructors Are Broken
Constructors promise a safe, guaranteed way to initialize objects in C++. But what they actually provide is many subtle opportunities to read uninitialized memory, and ergonomic issues that can negatively affect performance (by needing to throw exceptions to signal failure) and memory usage (by needing to juggle class layout in order to initialize members in the right order in the constructor initializer list). Join me as we spelunk into some of these topics and learn a healthy alternative from Rust, the factory function, that might help our code be safer and better.
Special guest appearances from struct update syntax/functional record update, non-static data member initializers/NSDMI, Non...
Просмотров: 101 232

Видео

Cursed C++ Casts
Просмотров 70 тыс.8 месяцев назад
Join me for an October scary story as we try to find the identity of the scariest C cast. The original inspiration for implementing public_cast came from here. I wrote my own version which is more modern and (IMHO) easier to understand, but I take no credit for the ingenuity (and devilishness) of the basic implementation technique. github.com/libretro/bnes-libretro/blob/master/nall/public_cast....
Two Ways To Do Dynamic Dispatch
Просмотров 71 тыс.8 месяцев назад
Rust and C both have built-in (but different-flavored) support for dynamic dispatch, and both also let you open the hood and implement it the other way that's not built-in. In this one we look at both languages' approaches, weigh the pros and cons, and ultimately come away seeing that they're both the right choice in different situations (isn't that just so unsatisfying and typical). Special gu...
5 Strong Opinions On Everyday Rust
Просмотров 59 тыс.9 месяцев назад
In more ways than one, programmers are born to have strong opinions about tiny things, and here are 5 of mine. Follow these 5 tips or disagree with them vehemently. Up to you, really. Special guest appearances from semicolons, Rust's most nefarious bug-waiting-to-happen, immutable references in both Rust and C , "consuming traits" and inner functions, and unsafe code (spooky). unsafe_op_in_unsa...
The Dark Side of .reserve()
Просмотров 146 тыс.10 месяцев назад
.reserve(...) is a method you might've seen in the API of your favorite dynamic array (or hash table or whatnot), and it's an excellent tool for making simple, impactful performance optimizations while you are building up data structures. But just like all tools, it has sharp edges. In this video we'll dive into where .reserve() can make your performance sing and where it can be devastating. Sp...
A Simpler Way to See Results
Просмотров 98 тыс.10 месяцев назад
Result is Rust's error handling type that is often discussed in the same (or similar) breath as Option, but it feels... scarier. But in this video, I'll argue that that relationship with Option is a useful piece to focus on; they are isomorphic in certain circumstances, and even when they're not, you can think of them as coexisting on a continuum. Then we'll talk about the dreaded/beloved quest...
Rust Functions Are Weird (But Be Glad)
Просмотров 128 тыс.11 месяцев назад
Rust takes a unique approach to function types, for both closures and fn items. In this video we'll talk about a way to fit these strange function types into your existing understanding of what types are. Then we'll look at how another language (okay, it's C ) does function types in a way that causes poor codegen in generic higher-order functions if you aren't careful and how/why Rust avoids th...
Choose the Right Option
Просмотров 66 тыс.11 месяцев назад
"Reference-to-Option" and "Optional-reference"* are both ways to represent "a reference to a value that may or may not be there" in Rust. So which one should you use in your APIs? Hear me spoil my answer within the first 10 seconds, and then stick around to get real nerdy about a topic you may have never spent 15 minutes thinking about before. Featuring special guest appearances from memory lay...
Use Arc Instead of Vec
Просмотров 136 тыс.Год назад
Rust lets you do efficient reference-counted strings and dynamic arrays using Arc basically just as easily as their owning (and deep-cloning) equivalents, String and Vec respectively. So why not use them as a reasonable default, until you actually need the mutability that String and Vec provide? Get into the weeds with me here, feat. some cool visualizations, with special guest appearance from ...
UE5 - Shuffle Using RandomStream (Blueprints Only Edition)
Просмотров 1,7 тыс.Год назад
In this video, we talk about how to shuffle an array using the Fisher-Yates algorithm, and then we implement that algorithm in pure Blueprints. The end result is a generic reusable macro that can shuffle ANY type of array, provided a RandomStream as input. This was neat; maybe I'll end up doing some more videos on implementing useful generic array algorithms that are missing from the Kismet Arr...
UE5 - Shuffle an Array Using RandomStream
Просмотров 1,2 тыс.Год назад
RandomStream is a super useful tool in Unreal Engine for generating reproducible random numbers from a single seed. But it doesn't come out of the box with a way to shuffle an array (or vector, or other random-access container). Here I show how to adapt it to the C 11 Uniform Random Bit Generator named requirement so that it can be used directly with C 11's std::shuffle algorithm. Hope you enjo...
UE5 - Spline Railing with Geometry Script and C++ (Part I)
Просмотров 3,2 тыс.Год назад
I built this cool rounded railing using some trial-and-error, math, and fancy Unreal Engine 5.1 geometry script tools. I decided to record this tutorial after I had some success programmatically modifying the spline from C (which happens in Part II). It makes the railing shape simple to edit (just a single spline) while still boasting its nice rounded shape. I'd be interested in seeing a Bluepr...
UE5 - Spline Railing with Geometry Script and C++ (Part II)
Просмотров 1,8 тыс.Год назад
I built this cool rounded railing using some trial-and-error, math, and fancy Unreal Engine 5.1 geometry script tools. I decided to record this tutorial after I had some success programmatically modifying the spline from C . It makes the railing shape simple to edit (just a single spline) while still boasting its nice rounded shape. I'd be interested in seeing a Blueprint-only version of this, ...

Комментарии

  • @edsparr2798
    @edsparr2798 4 дня назад

    I feel like result represents a genuine failure of the function more than option. For instance, HashSet::get does not always have a value for a certain key and that is expected behavior when consuming that API. In contrast, parsing a string and getting a UTF8Error is not something that you would expect to happen and signals that a genuine exception can happen. HashSet::get can not fail in the same way since a None return is expected behavior sometimes and not considered an error like a string parsing error. Do you agree?

  • @guavavodka
    @guavavodka 5 дней назад

    i wish i were good enough at rust to care about things this meaningless 💀

  • @dogeofvenice5624
    @dogeofvenice5624 5 дней назад

    That's an eye-opening way to think about rust. Great work.

  • @headlibrarian1996
    @headlibrarian1996 6 дней назад

    Two-phase initialization was popular in Java so we have a bunch of legacy C++ that has initialize() and finalize() methods. Really ugly. Plus we’re stuck on ‘17 so no designated initializers 😢

  • @theshooter89
    @theshooter89 9 дней назад

    I love the way you talk about hugely complex things in such a clean and clear, understandable way. One of the best rust channel on yt

  • @dmaster20ify
    @dmaster20ify 9 дней назад

    Man you have to sit in to get the point of these videos.

  • @NOT_A_ROBOT
    @NOT_A_ROBOT 12 дней назад

    9:22 isn't that similar to how most languages use exceptions, though?

  • @dmaster20ify
    @dmaster20ify 12 дней назад

    This is not how Cpp does dynamic dispatch. Cpp has no rules how polymorphism must be implemented.

  • @dmaster20ify
    @dmaster20ify 12 дней назад

    First let me thank you for that public_cast code. I never knew Cpp allowed that. I had a raw_cast that I had to implement class by class. Now I can just use public_cast and not have to manually implement anything. Second thing is; I always use C style cast, unless I need a specific cast. And I always use C style cast as a static cast or dynamic cast.

    • @_noisecode
      @_noisecode 12 дней назад

      Careful with that… C-style casts never do dynamic_casts, so if you’re looking for the safe+checked behavior of dynamic_cast, you won’t find it. All your C-style casts will always “succeed” but may give you a pointer you can’t safely dereference, and you won’t have any way to tell.

  • @djkato-personal2242
    @djkato-personal2242 13 дней назад

    I watch this video every time I'm about to write a library to remind myself how to do it right, and every time I learn and remember a bit more. My journey of errors was Box<dyn Error>, anyhow, thiserror, and I'm probably right in that most crustaceans go through this cycle lol. Great vid, thanks. Wish I had it when I started with rust.

  • @sxxbxx2197
    @sxxbxx2197 13 дней назад

    Rust indirections is three as well since functions need access to the object in 99.9% of cases.

    • @_noisecode
      @_noisecode 12 дней назад

      It’s true, you do usually need to load the object ptr; but in the video I made the observation (that I only skimmed over briefly to be fair) that in C++ there’s a _data dependency_ where we need to finish loading the object before we even know where the vptr is pointing. In the Rust layout we have both the object ptr and the vptr right from the start and the CPU can pipeline the memory accesses and subsequent operations better.

  • @samlovescoding
    @samlovescoding 14 дней назад

    Where can I buy your Rust course?

  • @brunskiy
    @brunskiy 15 дней назад

    Thank you! SplineCraft PRO plugin is advanced tool that available in the Marketplace - it also solves similar problems.

  • @brunskiy
    @brunskiy 15 дней назад

    SplineCraft PRO plugin is advanced tool that available in the Marketplace - it also solves similar problems.

  • @drew-et1mm
    @drew-et1mm 18 дней назад

    I watch this video once a month

  • @atesztoth
    @atesztoth 18 дней назад

    … and you just single handedly convinced me to like & subscribe with the bell turned to the max without even asking. Is it a (TotalSubscriber*) randomViewer? 🤔🤔🤔

  • @T1Oracle
    @T1Oracle 19 дней назад

    You don't need Rust in C++, you just need to use Rust.

  • @IllidanS4
    @IllidanS4 19 дней назад

    Sounds like a good rule of thumb would be this: could you replace each AddPoint with AddPoints on a 1-sized array without a performance drop (irrespective of the heap allocation)?

  • @IllidanS4
    @IllidanS4 19 дней назад

    I don't really think that the performance issue in C++ has to do with the fact that functions are "structurally typed". Sure, having a unique type for each function would simplify the optimization, but I'd fully expect the compiler here to acknowledge that you are passing the same function each time. It does so for normal values, so why not functions? Regardless, it is relatively uncomplicated to fix this without changing too much of your code, here's just quick code that I wrote that should help: template <auto Func> constexpr const inline auto monomorphic = [](auto&&... args) { return Func(std::forward<decltype(args)>(args)...); }; Using monomorphic<f> instead of f gives you the benefit of the lambda type, while not sacrificing much of readability or conventions.

  • @IllidanS4
    @IllidanS4 19 дней назад

    Pedantic complaint about the compiler's wording ‒ "no two closures, even if identical, have the same type" is technically incorrect. In the following code: let mut local = |x: i32| x; let local2 = local; local = local2; assert!(local.type_id() == local2.type_id()); The variables "local" and "local2" are our two closures, obviously "identical" (meaning "one and the same") since they come from the same definition, yet one can be assigned to the another, and their types are equal. It would be better to say something like "the type of a closure is unique and is not equal to the type of any other closure, even with equivalent definition".

  • @correabuscar
    @correabuscar 22 дня назад

    this video makes me glad that I'm not smart enough to know C++

  • @kirillandrianov953
    @kirillandrianov953 23 дня назад

    why have you marked the c++ function no-inline and then complained about the function not inlined(?) makes no sense to me basically, this example you wrote is perfectly inlinable and the generated code should be almost the same as the rust one upd: I found the explanation comment

  • @xmorse
    @xmorse 24 дня назад

    I would use ArrayString stored on the stack so you have copy trait and can forget about ownership

  • @gaeel330
    @gaeel330 24 дня назад

    I really enjoyed this video, I've used a bunch of languages with constructors (C++, Java, C#) and have been using Rust for a little while now. The other day, I had a little C# to write, and when working with constructors I felt a familiar tiny little pang of dread again, and I think you just nailed down what I was feeling. The metaphor that a constructor is a function that returns an initialised object isn't quite true, and it's in the details where some subtle ambiguities and bugs often lie. Rust applies the metaphor much more literally, and I feel a lot more confident in what I'm doing. Funnily enough, this is one of the things I've appreciated the most with Rust. There are much fewer "things" to consider in the language. It's closer to C in many ways in that regard.

  • @JoshBlasy
    @JoshBlasy 26 дней назад

    One of the biggest learning curves I've experienced, going from languages like Python on JS to rust, is forcing myself to trust the compiler. I don't have to do anything convoluted, I just write code and the compiler does its job. This video is a good example of that

  • @Lircking
    @Lircking 28 дней назад

    epic video

  • @SamualN
    @SamualN 28 дней назад

    5:09 this breaks my brain

  • @nickdarker3832
    @nickdarker3832 29 дней назад

    Fantastic video explaining the algorithm and it's implementation!

  • @TheJaguar1983
    @TheJaguar1983 Месяц назад

    Talking about Rust and C++ in the same video without bagging one out as superior to the other? Inconceivable! 😂

  • @vechnonedovolen666
    @vechnonedovolen666 Месяц назад

    11:39 what "int C::*" mean ?

  • @TekExplorer
    @TekExplorer Месяц назад

    What the hell. I am never using c++. Not fking worth it.

  • @vk8a8
    @vk8a8 Месяц назад

    I want Joe Biden to be youtuber

  • @lobovutare
    @lobovutare Месяц назад

    Just a small tip: you started talking about monomorphization as if everybody knows what that means. I had to look it up.

  • @Murukku47
    @Murukku47 Месяц назад

    Somehow C-style 'type(value)' casts always felt most aesthetic to me since it just looks like a function you give some other data type as an argument that then turns into the data type the function is named after.

  • @brockdaniel8845
    @brockdaniel8845 Месяц назад

    Maybe using consteval ?

  • @maxbd2618
    @maxbd2618 Месяц назад

    I don't do Rust but I like these great quality videos, however I'm a little critical on the part about returning direct references and applying that to other languages like C++. In C++ that probably isn't the best idea because you can end up with UAF bugs because a lot of times (especially returning views from functions) the view will live longer than the actual object that allocated the memory. Is that not a problem in Rust because of the borrow checker?

  • @phillipsusi1791
    @phillipsusi1791 Месяц назад

    I'll go one further... monster name strings likely are static anyhow, and so you don't even need Arc<str> or Box<str>, you can just use str directly. Then clones don't even need to increment a reference count on the heap, you just copy the pointer.

  • @dagoberttrump9290
    @dagoberttrump9290 Месяц назад

    Coming from c++ newly to rust, a few things don't make sense to me in this explanation. maybe someone can explain: - since we're talking about immutable vecs and string slices, why do we care for thread safety? if it can't be changed from either thread, we might as well just use Rc - even using just rc doesn't really make sense to me. after all, vectors and strings are already smartpointers. so wrapping them in another smartpointer just creates an unnecessary indirection. the clone should still be O(1) since if i understand correctly only stack metadata is copied, and copying 24 bytes instead of 16 really shouldn't make a big difference on modern cpu's - string slices in rust are probably implemented the same way as in c++, which would be ptr + size or a total of 16 bytes on x64. the compiler usually passes such small structs in registers, meaning passing a str directly without wrapping it in a smartpointer will always be faster. that's the whole point of slices actually. since the data likely resides in the rodata section of the binary, its lifetime is static furthermore, so it should always be valid.

    • @_noisecode
      @_noisecode Месяц назад

      Hey, thanks for the thoughtful comment! Here are my quick responses to your bullets: - You're right that the pointed-to data is safe from race conditions, since it's immutable--but the reference count itself still needs to be thread safe if the shared ownership is cloned around on multiple threads simultaneously. Rust provides Arc for this case, and Rc for when you don't need the overhead. - I might recommend you watch the video again I guess. One of the key points I made was that there actually ISN'T a double indirection when using Arc<str> (like there would be with Arc<String>)--the 'str' part means the Arc owns the str buffer directly. Also, cloning a String or Vec is not O(1)--String and Vec have unique ownership, so cloning is 'deep' and expensive. - Static strings are of course going to be simpler and probably faster, when they're an option. This video discusses an optimization for the case when you don't know the strings ahead of time.

  • @NilEoe
    @NilEoe Месяц назад

    Yes please, more interesting Rust content

  • @torsten_dev
    @torsten_dev Месяц назад

    I'd prefer a Cow<str>.

  • @meyou118
    @meyou118 Месяц назад

    ty - i like rust more and more once things are explain ed like this

  • @khronos142
    @khronos142 Месяц назад

    nice work

  • @Gennys
    @Gennys Месяц назад

    It's been very hard for me to decouple a lot of these very atomistic subjects from my original understanding of them coming from Java. All of these disparate concepts being baked in and "solved" with Java's particular inheritance model. The entire time I'm thinking to myself "Rust doesn't have simple interfaces?" xD I'm learning though, every day.

  • @woosix7735
    @woosix7735 Месяц назад

    Interesting interesting

  • @FishMan1nsk
    @FishMan1nsk Месяц назад

    Yo! This one is pretty usefull and there are not a lot of usefull geometry script videos on you tube. So thanks.

  • @norude
    @norude Месяц назад

    Do calls to NonZeroU8::new just get optimized out, because in memory it returns the same thing as it receives? If so, why would NonZeroU8::new_unchecked be necessary

    • @martingeorgiev999
      @martingeorgiev999 Месяц назад

      Under the hood the return values of both factories have the same memory layout but the compiler differentiates their types. i.e. when you call NonZeroU8::new() you get an Option which you have to handle or else you wouldn't be able to use the NonZeroU8 at all. In code: let x = NonZeroU8::new_unchecked(0); print!("{}", 1 / x.get()); causes a panic(crashes at runtime) In contrast: let x = NonZeroU8::new(0); print!("{}", 1 / x.get()); wouldn't compile because 'x' has type Option and you can't divide an integer by Option. You would have to do something like: let x = NonZeroU8::new(0); match x { Some(valid) => print!("{}", 1 / valid.get()); None => {} // there really is no NonZeroU8 to divide by }

  • @norude
    @norude Месяц назад

    What if there's more than one failure?

  • @npip99
    @npip99 Месяц назад

    So basically, all of the C programmers that use "null" have just been justified . But regardless, that's actually correct. Ideally, you want Rust to compile identical to C, but simply give you type-safety around your otherwise unsafe C code. C's NULL acts like an Optional<&T>, so you should use the same type in Rust.

  • @DeVibe.
    @DeVibe. 2 месяца назад

    Just use C++ 🤷‍♂️

  • @ultimatedude5686
    @ultimatedude5686 2 месяца назад

    I would argue that if you are exposing the function data_mut(&mut self) -> &mut Option<Data>, and you want to provide an immutable accessor, you should use data(&self) -> &Option<Data> for consistency. Though data_mut would be a somewhat strange method, and if you're already breaking encapsulation to that point you may as well just make your data field public.